#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include <omp.h>
#include <mpi.h>

#include "avl.h"
#include "preprocessor.h"

#define MAX  100
#define ROOT   0

static int total;

void send2more(int * next_node, int len, long res, int n);

void send2one(int * next_node, int len, long res, int n);

void count(node * t);

int main(int argc, char * argv[])
{
    MPI_File ifp;
    MPI_Comm comm = MPI_COMM_WORLD;
    MPI_Info info = MPI_INFO_NULL;
    MPI_Status status;
    MPI_Offset size;

    char word[MAX];
    char * buf;
    char letters[5];

    time_t time_bef;
    time_t time_aft;

    double seconds;

    long res;

    int error;
    int id;
    int n;
    int i;
    int j;
    int found;
    int num_letters;

    node * file_words = NULL;
    node * generated_words = NULL;
    node * aux = NULL;

    time(&time_bef);

    error = MPI_Init(&argc, &argv);
    if(error != MPI_SUCCESS) {
        printf("Error starting MPI program. Terminating.\n");
        MPI_Finalize();
        exit(1);
    }

    MPI_Comm_size(comm, &n);
    MPI_Comm_rank(comm, &id);

    if(argc != 2)
    {
        printf("Usage: $random_mpi_mp [file]\n");
        MPI_Finalize();
        exit(1);
    }

    printf("Loading the file in node %d...\n", id);
    error = MPI_File_open(comm, argv[1], MPI_MODE_RDONLY, info, &ifp);
    if(error != MPI_SUCCESS)
    {
        printf("Error loading the file. Terminating.\n");
        MPI_Finalize();
        exit(1);
    }

    MPI_File_get_size(ifp, &size);
    buf = (char *) malloc(size * sizeof(char));
    MPI_File_read_all(ifp, buf, size, MPI_CHAR, &status);

    printf("Building the tree in node %d...\n", id);
    res = 0;
    while(res <= size)
    {
        while(!isalnum(buf[res])) res++;

        i = 0;
        while(isalnum(buf[res]))
        {
            word[i] = tolower(buf[res]);
            res++;
            i++;
        }
        word[i] = '\0';

        file_words = insert(word, file_words);
    }

    count(file_words);
    found = 0;
    j = 1;
    srand(time(NULL) * id);

    while(found <= total)
    {
        //num_letters = (rand() % 5) + 1;
        num_letters = 5;

        #pragma omp parallel for
        for(i = 0; i < num_letters; i++)
            letters[i] = (rand() % 26) + 97;
        letters[i] = '\0';

        generated_words = insert(letters, generated_words);

        aux = findN(letters, file_words, num_letters);
        if(aux != NULL)
        {
            found += aux->rept;
        }

        if((float)(found / (1.0 * total)) >= 0.1 * j && j <= 10)
        {
            time(&time_aft);
            seconds = difftime(time_aft, time_bef);
            printf("%d0%%: %.3f sec\n", j, seconds);
            j++;
        }
    }

    printf("Cleaning all the mess in node %d...\n", id);
    free(buf);
    clear(file_words);
    clear(generated_words);
    MPI_File_close(&ifp);
    MPI_Finalize();

    time(&time_aft);
    seconds = difftime(time_aft, time_bef);
    printf("Time spent in node %d: %.3f sec\n", id, seconds);

    return 0;
}

void send2more(int *next_node, int len, long res, int n)
{
    MPI_Comm comm = MPI_COMM_WORLD;
    int tag = 0;

    int p1[2];
    int p2[2];

    p1[0] = res - len;
    p1[1] = len / 2;

    p2[0] = res - (len - p1[1]);
    p2[1] = len - p1[1];

    MPI_Send(&p1, 2, MPI_INT, *next_node, tag, comm);
    (*next_node)++;
    *next_node = (*next_node % (n - 1)) + 1;

    MPI_Send(&p2, 2, MPI_INT, *next_node, tag, comm);
    (*next_node)++;
    *next_node = (*next_node % (n - 1)) + 1;
}

void send2one(int *next_node, int len, long res, int n)
{
    MPI_Comm comm = MPI_COMM_WORLD;
    int tag = 0;

    int p1[2];

    p1[0] = res - len;
    p1[1] = len;

    MPI_Send(&p1, 2, MPI_INT, *next_node, tag, comm);
    (*next_node)++;
    *next_node = (*next_node % (n - 1)) + 1;
}

void count(node *t)
{
    if(t == NULL)
        return;

    total += t->rept;

    count(t->left);
    count(t->right);
}
